package com.ruoyi.project.app.service.impl;

import com.alibaba.druid.pool.DruidPooledResultSet;
import com.ruoyi.project.app.domain.Msg;
import com.ruoyi.project.app.domain.Order;
import com.ruoyi.project.app.domain.UserEntity;
import com.ruoyi.project.app.mapper.MsgMapper;
import com.ruoyi.project.app.mapper.OrderMapper;
import com.ruoyi.project.app.mapper.UserMapper;
import com.ruoyi.project.app.service.UserService;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.BaseExecutor;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

/**
 * 用户服务实现类
 *
 * @author Wwwwei
 */
@Service
public class UserServiceImpl implements UserService {
    @Autowired
    public UserMapper userDao;
    @Autowired
    public OrderMapper orderMapper;
    @Autowired
    public MsgMapper msgMapper;

    public Integer createUser(UserEntity userEntity) {
        return userDao.insert(userEntity);
    }

    public Integer updateUser(UserEntity userEntity) {
        return userDao.update(userEntity);
    }

    public Integer deleteUser(Integer id) {
        return userDao.delete(id);
    }

    public UserEntity getUserById(Integer id) {
        return userDao.selectById(id);
    }

    @Transactional
    public void test(int con){
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        for(int i=con;i<con+100;i++){
            Order order = new Order();
            order.setId(i);
            order.setName(Thread.currentThread().getName()+"---"+i);
            orderMapper.updateById(order);
        }

        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    @Transactional
    public void test1(int i) {
        Order order = new Order();
        order.setId(i);
        order.setName("ds"+i);
        orderMapper.updateById(order);
        try {
            Thread.sleep(10000000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    //REQUIRED(默认是这个)：这个方法必须运行在事务中，如果事务存在，则会运行在当前事务中，否则会新开一个事务。
    //SUPPORTS：这个方法不需要事务，但是如果存在事务，就在事务中运行。
    //MANDATORY：该方法必须在事务中运行，如果没有事务，抛异常IllegalTransactionStateException
    //REQUIRES_NEW：该方法必须运行在新事务中
    //NOT_SUPPORTED：不应该运行在事务中
    //NEVER:该方法不能运行在事务中，如果存在事务，抛异常
    //NESTED：
    // 如果当前存在事务，则该方法会在嵌套事务中运行，该嵌套事务可以独立于当前事务进行提交回滚操作。
    //如果当前不存在事务，会新开一个事务

     //前提：内层事务RuntimeException

    //外层事务  内层事务 调用方式  结果
    //require  require  this    全部回滚
    //require  无      this    全部回滚
    //无     require   this    全部都有数据
    //无     require   aop     外层事务有数据，内层无
    //require  无      aop     全部回滚

    //require  require aop     全部回滚
    //require  SUPPORTS aop     全部回滚
    //require  MANDATORY aop     全部回滚
    //require  REQUIRES_NEW aop     全部回滚
    //require  NOT_SUPPORTED aop     外层事务回滚，msg有数据
    //require  NEVER aop     全部回滚，内层事务直接抛异常IllegalTransactionStateException
    //require  NESTED aop     全部回滚
    @Autowired
    UserServiceImpl2 userServiceImpl2;

    @Transactional
    public void createOrder(Order order) {
//        orderMapper.save(order);

        Msg msg = null;

//        msg = msgMapper.getByMsg("1");
//        msg = msgMapper.selectById(1);
        System.out.println(orderMapper.selectById(1));
        System.out.println(msg+"~____");

        userServiceImpl2.sendMsg();

        msg = msgMapper.getByMsg("1");
//        msg = msgMapper.getByMsg1("1",System.currentTimeMillis());
        System.out.println(msg);
    }


    //CacheKey,BaseExecutor,query  ,DruidPooledResultSet
//    UserService proxy= (UserService) AopContext.currentProxy();
//    proxy.sendMsg();

    @Transactional(propagation = Propagation.NOT_SUPPORTED)
    public void sendMsg(){
        msgMapper.save(new Msg("1"));
        //调用短信网关
//        throw new RuntimeException();//运行时异常，编译时上层可以不捕捉
    }

    //看需求来：

    // 如果希望内层事务出运行时异常不影响外层事务的提交
    // 可以外层使用REQUIRED,内层使用REQUIRES_NEW或者NESTED，然后外层再用try catch处理该运行时异常
    //如果外层使用REQUIRED，内层不适用上面两个的话，会出现UnexpectedRollbackException
    //因为内层出异常了，事务会把状态标记为rollback-only,但是外层异常却try catch正常执行完了，此时矛盾，就会报异常

    //如果需要内外层同时成功，同时失败的话：
    // require require
    // require SUPPORTS
    // require MANDATORY


    @Async
    public void testAsync() {
        UserService proxy= (UserService) AopContext.currentProxy();
        proxy.testAsync1();
        System.out.println("testAsync");
    }

    @Async
    public void testAsync1() {
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("testAsync1");
    }

    public String getUserInfo() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return "userInfo";
    }


    public void anno() {
        System.out.println("anno");
        anno1();
    }

    public void anno1() {
        System.out.println("anno1");
    }

    public void save(UserEntity user) {
        userDao.save(user);
    }

    @Transactional
    public void testTran(Order order, Msg msg) {
        orderMapper.save(order);
        msgMapper.save(msg);

        //int i =1/0; 会回滚 ArithmeticException
//        order = null;
//        System.out.println(order.getId());会回滚 NullPointerException

        //throw new RuntimeException();//会回滚

        //throw new NullPointerException();//会回滚

//        throw new IndexOutOfBoundsException();//会回滚

        //中上所述 只有运行时异常才能被回滚，常见的有空指针，算术异常，越界异常
    }


    @Autowired
    private ApplicationEventPublisher publisher;

    @Transactional
    @Override
    public void pub() {
        UserEntity user = new UserEntity();
        user.setUsername("lry");
        user.setPassword("dsfadsafasd");
        userDao.save(user);

        //此事件根據事務的成功與否來決定是否回調@TransactionalEventListener(fallbackExecution = true)
        publisher.publishEvent(user.getUsername());

       // throw new RuntimeException();
    }
}
